﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using ICSharpCode.TextEditor;
using ICSharpCode.TextEditor.Document;
using ZackFlame.SimpleBrainFuck.Core;
using ZackFlame.SimpleBrainFuck.TextResources;

namespace ZackFlame.SimpleBrainFuck
{
    public partial class MainForm : Form
    {
        readonly string Title;

        SimpleCoder simpleCoder = new SimpleCoder();
        TextAreaUpdate lastError = null;
        
        bool isSavedCode = false;
        bool isSavedBrainFuck = false;
        string codeFileName;
        string brainFuckFileName;

        public MainForm()
        {
            InitializeComponent();

            Title = this.Text;
            UpdateWindowTitle();
            MainForm_Resize(null, EventArgs.Empty);
        }

        private void MainForm_Shown(object sender, EventArgs e)
        {
            buttonTranslate.Focus();
        }

        private void MainForm_Resize(object sender, EventArgs e)
        {
            if (!this.ClientSize.IsEmpty)
            {
                mainSplit.SplitterDistance = (this.ClientSize.Width + leftCenterSplit.Panel2.Width) / 2;
                actionPanel.Top = (this.ClientSize.Height - actionPanel.Height) / 2;
                
                Padding margin = brainFuckToolStripMenuItem.Margin;
                margin.Left = mainSplit.SplitterDistance - codeToolStripMenuItem.Width;
                brainFuckToolStripMenuItem.Margin = margin;
            }
        }

        private void buttonHelp_Click(object sender, EventArgs e)
        {
            MessageBox.Show(string.Format(
                "$zero - обнуление текущей ячейки" +
                "{0}" +
                "{0}$shift k - cдвиг на k ячеек вправо или на -k ячеек влево" +
                "{0}" +
                "{0}$add k - прибавление значения ячейки n (текущей) к значению ячейки n+k" +
                "{0}" +
                "{0}$sub k - вычитает значение ячейки n (текущей) из значения ячейки n+k" +
                "{0}" +
                "{0}$move k - копирование значения ячейки n (текущей) в ячейку n+k" +
                "{0}с потерей (обнулением) значения ячейки n" +
                "{0}" +
                "{0}$copy k t - копирование значения ячейки n (текущей) в ячейку n+k" +
                "{0}c использованием промежуточной ячейки n+k+t, благодаря чему" +
                "{0}значение ячейки n не теряется (сохраняется)" +
                "{0}" +
                "{0}if t" +
                "{0}  <операторы>" +
                "{0}else" +
                "{0}  <операторы>" +
                "{0}end" +
                "{0} - оператор выбора на основе текущей ячейки n (если > 0, то истина)," +
                "{0}где n+t - служебная ячейка" +
                "{0}" +
                "{0}$repeat text k - повторяет заданную строку text k раз" +
                "{0}" +
                "{0}$value k t - компактный ввод константы  k (не больше 256) с" +
                "{0}использованием промежуточной ячейки n+t, где n - текущая ячейка" +
                "{0}" +
                "{0}$text text - ввод строки символов text длиной len, начиная с текущей" + 
                "{0}ячейки n, с использованием промежуточной ячейки n+len" +
                "{0}" +
                "{0}$type text t - печать строки символов text с использованием промежуточной" +
                "{0}ячейки n+t, где n - текущая ячейка" +
                "{0}" +
                "{0}$print k - печать строки символов длиной k (при k < 0 - печать в" +
                "{0}обратном порядке)", Environment.NewLine));
        }

        private void buttonTranslate_Click(object sender, EventArgs e)
        {
            if (lastError != null)
            {
                textEditorCode.Document.MarkerStrategy.RemoveAll(m => true);
                textEditorCode.Document.RequestUpdate(
                    new TextAreaUpdate(TextAreaUpdateType.WholeTextArea));
                textEditorCode.Document.CommitUpdate();
                lastError = null;
            }

            try
            {
                string code = simpleCoder.Translate(textEditorCode.Document.TextContent);

                if (checkBoxOptimize.Checked || radioButtonUnlimited.Checked)
                    code = code.Replace(Environment.NewLine, string.Empty);

                if (checkBoxOptimize.Checked)
                    code = simpleCoder.Optimize(code);

                if (radioButtonFixed.Checked)
                    code = simpleCoder.SetLineLength(code, (int)numericUpDownLineLength.Value);

                textEditorBrainFuck.Document.TextContent = code;
                textEditorBrainFuck.Document.RequestUpdate(
                    new TextAreaUpdate(TextAreaUpdateType.WholeTextArea));
                textEditorBrainFuck.Document.CommitUpdate();
            }
            catch (CodeErrorException ex)
            {
                LineSegment line = textEditorCode.Document.GetLineSegment(ex.Line);
                textEditorCode.Document.MarkerStrategy.AddMarker(
                    new TextMarker(line.Offset, line.Length, TextMarkerType.WaveLine));

                lastError = new TextAreaUpdate(TextAreaUpdateType.SingleLine, ex.Line);
                textEditorCode.Document.RequestUpdate(lastError);
                textEditorCode.Document.CommitUpdate();
                
                MessageBox.Show(string.Format("Line {0}: {1}", ex.Line + 1, ex.Message));
            }
        }

        private void buttonCopy_Click(object sender, EventArgs e)
        {
            try
            {
                if (textEditorBrainFuck.Document.TextLength > 0)
                {
                    DataObject clipboardData = new DataObject(
                        textEditorBrainFuck.Document.TextContent);

                    Clipboard.SetDataObject(clipboardData, true, 5, 10);
                }
            }
            catch (ExternalException)
            {
            }
        }

        private void checkBoxOptimize_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBoxOptimize.Checked)
            {
                if (radioButtonNoChange.Checked)
                    radioButtonFixed.Checked = true;

                radioButtonNoChange.Enabled = false;
            }
            else
            {
                radioButtonNoChange.Enabled = true;
            }
        }

        #region Menu Strips

        private void createCode_Click(object sender, EventArgs e)
        {
            CreateCode();
        }

        private void openCode_Click(object sender, EventArgs e)
        {
            if (openCodeDialog.ShowDialog() == DialogResult.OK)
            {
                OpenCode(openCodeDialog.FileName);
            }
        }

        private void saveCode_Click(object sender, EventArgs e)
        {
            if (isSavedCode)
            {
                SaveCode(codeFileName);
            }
            else if (saveCodeDialog.ShowDialog() == DialogResult.OK)
            {
                SaveCode(saveCodeDialog.FileName);
            }
        }

        private void saveCodeAs_Click(object sender, EventArgs e)
        {
            if (saveCodeDialog.ShowDialog() == DialogResult.OK)
            {
                SaveCode(saveCodeDialog.FileName);
            }
        }

        private void exit_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void saveBF_Click(object sender, EventArgs e)
        {
            if (isSavedBrainFuck)
            {
                SaveBrainFuck(brainFuckFileName);
            }
            else if (saveBrainFuckDialog.ShowDialog() == DialogResult.OK)
            {
                SaveBrainFuck(saveBrainFuckDialog.FileName);
            }
        }

        private void saveBFAs_Click(object sender, EventArgs e)
        {
            if (saveBrainFuckDialog.ShowDialog() == DialogResult.OK)
            {
                SaveBrainFuck(saveBrainFuckDialog.FileName);
            }
        }

        private void run_Click(object sender, EventArgs e)
        {
            brainFuckInteractiveConsole1.RunCode(textEditorBrainFuck.Document.TextContent);
        }

        private void reset_Click(object sender, EventArgs e)
        {
            brainFuckInteractiveConsole1.Reset();
        }

        #endregion

        private void UpdateWindowTitle()
        {
            this.Text = Title + " - " +
                (isSavedCode ? Path.GetFileName(codeFileName) : MainFormText.File_UntitledFile) +
                (isSavedBrainFuck ? " - " + Path.GetFileName(brainFuckFileName) : string.Empty);
        }

        public void CreateCode()
        {
            textEditorCode.Document.TextContent = string.Empty;
            textEditorCode.Document.RequestUpdate(
                new TextAreaUpdate(TextAreaUpdateType.WholeTextArea));
            textEditorCode.Document.CommitUpdate();
            
            isSavedCode = false;
            isSavedBrainFuck = false;
            UpdateWindowTitle();
        }

        public void OpenCode(string fileName)
        {
            textEditorCode.Document.TextContent = File.ReadAllText(fileName);
            textEditorCode.Document.RequestUpdate(
                new TextAreaUpdate(TextAreaUpdateType.WholeTextArea));
            textEditorCode.Document.CommitUpdate();

            isSavedCode = true;
            codeFileName = fileName;
            UpdateWindowTitle();
        }

        public void SaveCode(string fileName)
        {
            File.WriteAllText(fileName, textEditorCode.Document.TextContent);
            isSavedCode = true;
            codeFileName = fileName;
            UpdateWindowTitle();
        }

        public void SaveBrainFuck(string fileName)
        {
            File.WriteAllText(fileName, textEditorBrainFuck.Document.TextContent);
            isSavedBrainFuck = true;
            brainFuckFileName = fileName;
            UpdateWindowTitle();
        }
    }
}
